import numpy as np


def interpret_bitmask(
    nda_data: np.ndarray, starting_bit: int, length_bits: int
) -> np.ndarray:
    """Interpret the values in the given bitmask array's bit range

    Parameters
    ----------
    nda_data : np.ndarray
        The given bitmask ndarray.
    starting_bit : int
        Starting bit for the bit range to interpret, 0-based.
    length_bits : int
        Length of bit range to interpret.

    Returns
    -------
    np.ndarray
        The interpreted ndarray.

    Raises
    ------
    RuntimeError
        Raised when the given `starting_bits` and `length_bits` is invalid.

    Examples
    ---------
    USGS is using bitmask to store multiple masks in one 16-bit QA band for the LANDSAT8 Collection-2:

    ```
    - Bit 0: Fill
    - Bit 1: Dilated Cloud
    - Bit 2: Cirrus (high confidence)
    - Bit 3: Cloud
    - Bit 4: Cloud Shadow
    - Bit 5: Snow
    - Bit 6: Clear
        - 0: Cloud or Dilated Cloud bits are set
        - 1: Cloud and Dilated Cloud bits are not set
    - Bit 7: Water
    - Bits 8-9: Cloud Confidence
        - 0: None
        - 1: Low
        - 2: Medium
        - 3: High
    - Bits 10-11: Cloud Shadow Confidence
        - 0: None
        - 1: Low
        - 2: Medium
        - 3: High
    - Bits 12-13: Snow/Ice Confidence
        - 0: None
        - 1: Low
        - 2: Medium
        - 3: High
    - Bits 14-15: Cirrus Confidence
        - 0: None
        - 1: Low
        - 2: Medium
        - 3: High
    ```

    Assuming already extract the bitmask band for a LANDSAT8 L2 scene as `nda_landsat8_bitmask`,
    then extract several masks from the ndarray.

    >>> # extract the bit starting from the 0 bit for 1 bit length as the nodata mask
    >>> nodata_mask = interpret_bitmask(nda_landsat8_bitmask, 0, 1)
    >>> # extract the bit starting from the 3rd bit for 1 bit length as the cloud mask
    >>> cloud_shadow_mask = interpret_bitmask(nda_landsat8_bitmask, 3, 1)
    >>> # extract the bit starting from the 5th bit for 1 bit length as the snow mask
    >>> snow_mask = interpret_bitmask(nda_landsat8_bitmask, 5, 1)
    >>> # extract the bit starting from the 8th bit for 2 bits length as the cloud confidence
    >>> cloud_confidence = interpret_bitmask(nda_landsat8_bitmask, 8, 2)
    """
    full_length = nda_data.dtype.itemsize * 8

    if starting_bit < 0:
        raise RuntimeError("Not supporting negative `starting_bit`")
    elif starting_bit > full_length:
        raise RuntimeError(
            (
                f"`starting_bit` must be smaller than the full number of bits {full_length} "
                "for the dtype of the given `nda_data`"
            )
        )
    if starting_bit + length_bits > full_length:
        raise RuntimeError(
            (
                "The bit range specified by `starting_bit` and `length_bits` ",
                f"is more than full number of bits {length_bits} for the dtype of the given `nda_data`",
            )
        )

    mask_value = 2 ** length_bits - 1
    if starting_bit > 0:
        nda_data = np.right_shift(nda_data, starting_bit)
    nda_res = np.bitwise_and(nda_data, mask_value)
    return nda_res
